package com.asialjim.wechat.spring.config.db;

import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.type.JdbcType;

import java.util.Objects;

import static java.util.stream.Collectors.joining;

/**
 * =====================================
 * <br/>
 * 数据库更新
 * <br/>
 * =====================================
 *
 * @Author Asial Jim
 * @email zengyz0128@zsmarter.com
 * @Date 2020/12/16 10:45
 * @Copyright (c) ztzh inc Ztzh Co., LTD
 */
public class ApplicationUpdate extends AbstractMethod {

    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        SqlMethod sqlMethod = SqlMethod.UPDATE;
        String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(),
                this.sqlSet(true, true, tableInfo, true, ENTITY, ENTITY_DOT),
                sqlWhereEntityWrapper(true, tableInfo), sqlComment());
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        return this.addUpdateMappedStatement(mapperClass, modelClass, getMethod(sqlMethod), sqlSource);
    }

    /**
     * 获取set语句sql脚本
     * @param logic
     * @param ew
     * @param table
     * @param judgeAliasNull
     * @param alias
     * @param prefix
     * @return
     */
    protected String sqlSet(boolean logic, boolean ew, TableInfo table, boolean judgeAliasNull, final String alias, final String prefix) {
        String sqlScript = this.getAllSqlSet(table,logic, prefix);
        if (judgeAliasNull) {
            sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format("%s != null", alias), true);
        }
        if (ew) {
            sqlScript += NEWLINE;
            sqlScript += SqlScriptUtils.convertIf(SqlScriptUtils.unSafeParam(U_WRAPPER_SQL_SET),
                    String.format("%s != null and %s != null", WRAPPER, U_WRAPPER_SQL_SET), false);
        }
        sqlScript = SqlScriptUtils.convertSet(sqlScript);
        return sqlScript;
    }

    /**
     * 获取所有更新字段的sql脚本
     * @param table
     * @param logic
     * @param prefix
     * @return
     */
    private String getAllSqlSet(final TableInfo table,final boolean logic, final String prefix){
        final String newPrefix = prefix == null ? EMPTY : prefix;
        return table.getFieldList().stream()
                .filter(i -> {
                    if (logic) {
                        return !(table.isWithLogicDelete() && i.isLogicDelete());
                    }
                    return true;
                }).map(i -> this.getSqlSet(i,false,newPrefix)).filter(Objects::nonNull).collect(joining(NEWLINE));
    }

    /**
     * 获取单个字段的更新sql脚本
     * @param table
     * @param ignoreIf
     * @param prefix
     * @return
     */
    public String getSqlSet(final TableFieldInfo table, final boolean ignoreIf, final String prefix) {
        final String newPrefix = prefix == null ? EMPTY : prefix;
        // 默认: column=
        String sqlSet = table.getColumn() + EQUALS;
        if (table.isWithUpdateFill() && table.getJdbcType() == JdbcType.DATE){
            sqlSet += "sysdate";
        } else if (StringUtils.isNotBlank(table.getUpdate())) {
            sqlSet += String.format(table.getUpdate(), table.getColumn());
        } else {
            sqlSet += SqlScriptUtils.safeParam(newPrefix + table.getEl());
        }
        sqlSet += COMMA;
        if (ignoreIf) {
            return sqlSet;
        }

        if (table.isWithUpdateFill()) {
            // 不进行 if 包裹
            return sqlSet;
        }
        return this.convertIf(table,sqlSet, this.convertIfProperty(newPrefix, table.getProperty()), table.getUpdateStrategy());
    }

    /**
     * 使用if条件包裹
     * @param tableFieldInfo
     * @param sqlScript
     * @param property
     * @param fieldStrategy
     * @return
     */
    private String convertIf(final TableFieldInfo tableFieldInfo,final String sqlScript, final String property, final FieldStrategy fieldStrategy) {
        if (fieldStrategy == FieldStrategy.NEVER) {
            return null;
        }
        if (tableFieldInfo.isPrimitive() || fieldStrategy == FieldStrategy.IGNORED) {
            return sqlScript;
        }
        if (fieldStrategy == FieldStrategy.NOT_EMPTY && tableFieldInfo.isCharSequence()) {
            return SqlScriptUtils.convertIf(sqlScript, String.format("%s != null and %s != ''", property, property),
                    false);
        }
        return SqlScriptUtils.convertIf(sqlScript, String.format("%s != null", property), false);
    }

    /**
     * 使用if条件包裹时使用‘[]’ 去实体属性
     * @param prefix    前缀，一般代表该数据库表映射实体实例
     * @param property  映射实体属性
     * @return
     */
    private String convertIfProperty(final String prefix,final String property) {
        return StringUtils.isNotBlank(prefix) ? prefix.substring(0, prefix.length() - 1) + "['" + property + "']" : property;
    }

}
